/*
* @Author: ystlong
* @Date:   2018-06-29 18:55:11
* @Last Modified by:   ystlong
* @Last Modified time: 2018-06-30 02:25:25
*/

#include <stdio.h>
#include <stdlib.h>
#include "parse_elf.h"
#include <assert.h>
#include <string.h>

static inline uint8_t char_2_uint8t(char c)
{
	if (c >= 'a' && c <= 'f')
		return c - ('a' - '9' + '0' - 1);
	else if (c >= 'A' && c <= 'F')
		return c - ('A' - '9' + '0' - 1);
	else if (c >= '0' && c <= '9')
		return c - '0';
	printf("error char_2_uint8t char format err: %c\n", c);
	exit(-20);
	return 0;
}

// patch_val 应为16进制字符串
static int parse_patch_val(char *patch_val, size_t val_len, uint8_t *buff)
{
	assert(val_len%2 == 0);
	for(int i; i<val_len; i+=2){
		buff[i/2] = char_2_uint8t(patch_val[i])<<4 | char_2_uint8t(patch_val[i+1]);
	}
	return val_len/2;
}

void patch_elf_file(FILE *elf_fs, long offset, char *patch_val)
{
	char buff[1024];
	// static int parse_patch_val(char *patch_val, size_t val_len, uint8_t *buff)
	if (strlen(patch_val)%2 != 0) {
		printf("patch_val val must be even len\n");
		exit(-12);
	}
	int len = parse_patch_val(patch_val, strlen(patch_val), buff);
	printf("use file offset: %d, write len: %d\n", offset, len);
	fseek(elf_fs, offset, SEEK_SET);
	fwrite(buff, len, 1, elf_fs);
	printf("patch finish\n");
}

long route_elf_type(FILE *elf_fs, int patch_type, char *target)
{
	unsigned char buf[5];
	long file_off = -1;
	fread(buf, 5, 1, elf_fs);
	rewind(elf_fs);
	switch(buf[4]){
		case ELFCLASS32:
			// printf("ELFCLASS32\n");
			file_off = elf32_parse(elf_fs, patch_type, target);
			break;
		case ELFCLASS64:
			// printf("ELFCLASS64\n");
			file_off = elf64_parse(elf_fs, patch_type, target);
			break;
		case ELFCLASSNONE:
		default:
			printf("ELFCLASSNONE\n");
			break;
	}
	return file_off;
}

int main(int argc, char const *argv[])
{
	const char *elf_name = NULL;
	long file_off = -1;
	FILE *elf_fs = NULL;
	int patch_type = -1;
	char *target;
	char *patch_val = NULL;
	if (argc < 4) {
	usage:
		printf("usage: %s elf_path [addr|func] [addr_val|func_name] [patch_val]\n", argv[0]);
		if(elf_fs != NULL) fclose(elf_fs);
		return 1;
	}

	elf_name = argv[1];
	elf_fs = fopen(elf_name, "r+");
	if(elf_fs == NULL) {
		perror("open file");
		goto usage;
	}
	if (strcmp(argv[2], "addr") == 0) {
		patch_type = PATCH_ADDR;
	}else if(strcmp(argv[2], "func") == 0) {
		patch_type = PATCH_FUN_NAME;
	}else{
		printf("op error: %s\n", argv[2]);
		goto usage;
	}

	target = argv[3];

	if (argc >= 5) {
		patch_val = argv[4];
	}
	
	file_off = route_elf_type(elf_fs, patch_type, target);

	if(patch_val){
		if (file_off == -1) {
			printf("unkown error: file offset get error -1\n");
			goto usage;
		}
		patch_elf_file(elf_fs, file_off, patch_val);
	}
	fclose(elf_fs);
	return 0;
}
